#!/bin/bash

#  Copyright (C) 2018-2022 LEIDOS.
#
#  Licensed under the Apache License, Version 2.0 (the "License"); you may not
#  use this file except in compliance with the License. You may obtain a copy of
#  the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#  License for the specific language governing permissions and limitations under
#  the License.

# Code below largely based on template from Stack Overflow:
# https://stackoverflow.com/questions/37257551/defining-subcommands-that-take-arguments-in-bash
# Question asked by user
# DiogoSaraiva (https://stackoverflow.com/users/4465820/diogosaraiva)
# and answered by user
# Charles Duffy (https://stackoverflow.com/users/14122/charles-duffy)
# Attribution here is in line with Stack Overflow's Attribution policy cc-by-sa found here:
# https://stackoverflow.blog/2009/06/25/attribution-required/


__pull_newest_carma_base() {
    if [ "$1" = "-d" ]; then
        local remote_image=$(wget -q https://registry.hub.docker.com/v1/repositories/usdotfhwastoldev/carma-base/tags -O -  | \
        sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n'  | \
        awk -F: '{print "usdotfhwastoldev/carma-base:"$3}' | tail -n 1)
    elif [ "$1" = "-c" ]; then
        local remote_image=$(wget -q https://registry.hub.docker.com/v1/repositories/usdotfhwastolcandidate/carma-base/tags -O -  | \
        sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n'  | \
        awk -F: '{print "usdotfhwastolcandidate/carma-base:"$3}' | tail -n 1)
    else
        local remote_image=$(wget -q https://registry.hub.docker.com/v1/repositories/usdotfhwastol/carma-base/tags -O -  | \
        sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n'  | \
        awk -F: '{print "usdotfhwastol/carma-base:"$3}' | tail -n 1)
    fi

    echo "No local carma-base image found, pulling down the most recent from Dockerhub..."
    docker pull $remote_image
}

##
# Helper function to print to stderr.
# This is useful for allowing bash methods to return strings while still communicating information to the user
##
echoerr() { echo "$@" 1>&2; }

##
# Method returns a fully specified docker image name and tag based on the currently set carma-config
# The method extracts DOCKER_ORG and DOCKER_TAG from the .env file in carma-config to look for
# combinations from SUPPORTED_IMAGES that exist locally.
# If the config is not set or an image cannot be found the method will return most recent
# SUPPORTED_IMAGES image as the default.
# If that fails, returns the most recent carma-base image.
# If none is possible from all above, returns error message.
# The user can specify optional images as sed compatable regex lists.
# For example calling
#  $(__get_image_from_config 'carma-platform\|carma-messenger')
##
__get_image_from_config() {
    # Default image is the most recent carma-base image by time of creation
    local DEFAULT_IMAGE=$(docker images --format '{{.Repository}}:{{.Tag}} {{.CreatedAt}}' | grep -E 'carma-base' | grep -v '<none>' | sort -k 2 -r | head -n 1 | awk '{print $1}')
    local SUPPORTED_ORANIZATIONS="usdotfhwastol\|usdotfhwastoldev\|usdotfhwastolcandidate"
    local SUPPORTED_IMAGES="carma-platform\|carma-messenger"

    if [ -n "$1" ]; then
        SUPPORTED_IMAGES="$1"
    fi

    # Check if carma-config is set. If so, use what was in the carma config
    if docker container inspect carma-config > /dev/null 2>&1; then

        # NOTE: Before and including carma-system-4.5.0, image can be directly grabbed from config docker-compose using sed
        # This approach needs to be used for versions for and before carma-system-4.5.0 because the /opt/carma/vehicle/config/.env file is not defined
        # sed command finds the first instance of an image which is part of the set defined in SUPPORTED_ORGANIZATIONS and SUPPORTED_IMAGES.
        # then returns the name of the image.
        local TARGET_IMAGE=$(docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
            "sed -n -e '/image:\s*\($SUPPORTED_ORANIZATIONS\)\/\($SUPPORTED_IMAGES\)/ s/.*\image: *//p' /opt/carma/vehicle/config/docker-compose.yml | grep -m 1 '.*'")

        # If the sed resulted in valid image, then return
        if [ -n "$TARGET_IMAGE" ]; then
            echo "$TARGET_IMAGE"
            return 0
        fi

        # NOTE: Else, possibly newer config than carma-system-4.5.0 which uses `/opt/carma/vehicle/config/.env`...
        local IMAGE_FOUND=0

        # Extract the .env file content from the carma-config volume
        docker run --rm --volumes-from carma-config:ro busybox:latest cat /opt/carma/vehicle/config/.env > /tmp/.env

        # Extract DOCKER_ORG and DOCKER_TAG from the .env file
        source /tmp/.env

        # 1. Construct the image name using the values from the .env file and given SUPPORTED_IMAGES options

        # Split SUPPORTED_IMAGES into an array
        local IFS='\|'
        read -ra IMAGE_ARRAY <<< "$SUPPORTED_IMAGES"

        for IMAGE in "${IMAGE_ARRAY[@]}"; do
            TARGET_IMAGE="${DOCKER_ORG}/${IMAGE}:${DOCKER_TAG}"

            # Verify target image exists
            if [ -n "$(docker images -q $TARGET_IMAGE 2> /dev/null)" ]; then
                IMAGE_FOUND=1
                break
            fi
        done

        # Check if TARGET_IMAGE exists locally
        if [ $IMAGE_FOUND -eq 0 ]; then
            echoerr "TARGET_IMAGE with DOCKER_ORG=${DOCKER_ORG} DOCKER_TAG=${DOCKER_TAG} does not exist locally for these images: ${SUPPORTED_IMAGES},
                trying to get most recent images from these: ${SUPPORTED_IMAGES}"

            # 2. Get most recent images of SUPPORTED_IMAGES
            for IMAGE in "${IMAGE_ARRAY[@]}"; do
                TARGET_IMAGE=$(docker images --format '{{.Repository}}:{{.Tag}} {{.CreatedAt}}' | grep -E "$IMAGE" | grep -v '<none>' | sort -k 2 -r | head -n 1 | awk '{print $1}')

                # Verify target image exists
                if [ -n "$(docker images -q $TARGET_IMAGE 2> /dev/null)" ]; then
                    IMAGE_FOUND=1
                    break
                fi
            done

            # 3. Get most recent carma-base image
            if [ $IMAGE_FOUND -eq 0 ]; then
            echoerr "Trying to use default image: ${DEFAULT_IMAGE}"
                TARGET_IMAGE=$DEFAULT_IMAGE
            fi
        fi

        rm /tmp/.env
    else
        echoerr "No config detected or target image specified so using default: $DEFAULT_IMAGE"
        TARGET_IMAGE=$DEFAULT_IMAGE
    fi

    if [ -z "$TARGET_IMAGE" ]; then
        echoerr "No recent carma-base image found, please download or build carma-base image locally."
    fi

    echo "$TARGET_IMAGE"
}
__get_most_recent_carma_base() {
    if [ "$1" = "-d" ]; then
        local USERNAME=usdotfhwastoldev
    elif [ "$1" = "-c" ]; then
        local USERNAME=usdotfhwastolcandidate
    else
        local USERNAME=usdotfhwastol
    fi
    docker image ls --format "{{.Repository}}:{{.Tag}}" "$USERNAME/carma-base" | grep -v "<none>$" | head -n 1
}

carma-config__set() {
    if [ "$1" = "-d" ]; then
        local USERNAME=usdotfhwastoldev
    elif [ "$1" = "-c" ]; then
        local USERNAME=usdotfhwastolcandidate
    else
        local USERNAME=usdotfhwastol
    fi
    if [[ -z $1 ]]; then
        echo "Please specify a tag string for carma-config to set."
        echo "Done."
        exit 1
    fi

    local IMAGE_NAME="$USERNAME/carma-config:$1"
    if [ ! -z "$(echo $1 | grep :)" ]; then
        IMAGE_NAME=$1
    fi

    if docker container inspect carma-config > /dev/null 2>&1; then
        echo "Clearing existing CARMA configuration instance..."
        carma__stop
        echo "Deleting old CARMA config..."
        docker rm carma-config
    fi

    echo "Setting $IMAGE_NAME as current CARMA configuration instance..."
    docker run --name carma-config "$IMAGE_NAME"
}

carma__attach() {
    # Extract the .env file content from the carma-config volume
    docker run --rm --volumes-from carma-config:ro busybox:latest cat /opt/carma/vehicle/config/.env > /tmp/.env 2>/dev/null  # suppress misleading error message that will occur for versions before 4.5.0

    # Extract DOCKER_ORG and DOCKER_TAG (and others) from the .env file
    set -o allexport
    source /tmp/.env 2>/dev/null # suppress misleading error message that will occur for versions before 4.5.0
    set +o allexport

    local CARMA_DOCKER_FILE="`docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c 'cat /opt/carma/vehicle/config/docker-compose.yml'`"

    echo "Attaching to CARMA container STDOUT..."
    echo "$CARMA_DOCKER_FILE" | docker-compose -p carma logs -f - logs --follow --tail=10

    rm /tmp/.env
}

carma-config__edit() {
    if ! docker container inspect carma-config > /dev/null 2>&1; then
        echo "No existing CARMA configuration found, nothing to edit. Please set a config."
        echo "Done."
        exit 1
    fi

    echo "Opening shell inside carma-config container with read/write privileges..."

    local carma_base=$(__get_image_from_config carma-base)
    if [[ -z $carma_base ]]; then
        __pull_newest_carma_base
        carma_base=$(__get_most_recent_carma_base)
    fi

    docker run -it --rm --volumes-from carma-config $carma_base bash
}

carma-config__inspect() {
    if ! docker container inspect carma-config > /dev/null 2>&1; then
        echo "No existing CARMA configuration found, nothing to inspect. Please set a config."
        echo "Done."
        exit 1
    fi

    echo "Opening shell inside carma-config container with read-only privileges..."

    local carma_base=$(__get_most_recent_carma_base)
    if [[ -z $carma_base ]]; then
        __pull_newest_carma_base
        carma_base=$(__get_most_recent_carma_base)
    fi

    docker run -it --rm --volumes-from carma-config:ro $carma_base bash
}

carma-config__reset() {
    if ! docker container inspect carma-config > /dev/null 2>&1; then
        echo "No existing CARMA configuration found, nothing to reset. Please set a config."
        echo "Done."
        exit 1
    fi

    local CURRENT_IMAGE=`docker container inspect --format='{{.Config.Image}}' carma-config`
    echo "Found current config: $CURRENT_IMAGE, resetting to base state"
    carma-config__set $CURRENT_IMAGE
}

carma-config__list_local() {
    echo "Locally installed images: "
    echo ""
    echo "usdotfhwastol images:"
    docker images usdotfhwastol/carma-config
    echo ""
    echo "usdotfhwastoldev images:"
    docker images usdotfhwastoldev/carma-config
    echo ""
    echo "usdotfhwastolcandidate images:"
    docker images usdotfwhastolcandidate/carma-config
}

carma-config__list_remote() {
    if [ "$1" = "-d" ]; then
        wget -q https://registry.hub.docker.com/v1/repositories/usdotfhwastoldev/carma-config/tags -O -  | \
        sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n'  | \
        awk -F: 'BEGIN {print "Remotely available images from usdotfhwastoldev Dockerhub:\nIMAGE\t\t\t\tTAG"} {print "usdotfhwastoldev/carma-config\t"$3}'
    elif [ "$1" = "-c" ]; then
        wget -q https://registry.hub.docker.com/v1/repositories/usdotfhwastolcandidate/carma-config/tags -O -  | \
        sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n'  | \
        awk -F: 'BEGIN {print "Remotely available images from usdotfhwastolcandidate Dockerhub:\nIMAGE\t\t\t\tTAG"} {print "usdotfhwastolcandidate/carma-config\t"$3}'
    else
        wget -q https://registry.hub.docker.com/v1/repositories/usdotfhwastol/carma-config/tags -O -  | \
        sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n'  | \
        awk -F: 'BEGIN {print "Remotely available images from usdotfhwastol Dockerhub:\nIMAGE\t\t\t\tTAG"} {print "usdotfhwastol/carma-config\t"$3}'
    fi
}

carma-config__install() {
    if [ "$1" = "-d" ]; then
        local USERNAME=usdotfhwastoldev
        shift
    elif [ "$1" = "-c" ]; then
        local USERNAME=usdotfhwastolcandidate
        shift
    else
        local USERNAME=usdotfhwastol
    fi

    local IMAGE_NAME="$USERNAME/carma-config:$1"
    if [ ! -z "$(echo $1 | grep :)" ]; then
        IMAGE_NAME=$1
    fi

    echo "Downloading $IMAGE_NAME..."
    docker pull $IMAGE_NAME
    echo "Building temporary container of $IMAGE_NAME to read dependency data..."

    if docker container inspect carma-config-tmp > /dev/null 2>&1; then
        echo "Cleaning up temporary containers from previous install..."
        docker rm carma-config-tmp
    fi
    docker run --name carma-config-tmp $IMAGE_NAME

    # Extract the .env file content from the carma-config volume
    docker run --rm --volumes-from carma-config-tmp:ro busybox:latest cat /opt/carma/vehicle/config/.env > /tmp/.env 2>/dev/null  # suppress misleading error message that will occur for versions before 4.5.0

    # Extract DOCKER_ORG and DOCKER_TAG (and others) from the .env file
    set -o allexport
    source /tmp/.env 2>/dev/null # suppress misleading error message that will occur for versions before 4.5.0
    set +o allexport

    local CARMA_DOCKER_FILE="`docker run --rm --volumes-from carma-config-tmp:ro --entrypoint sh busybox:latest -c 'cat /opt/carma/vehicle/config/docker-compose.yml' | sed s/carma-config/carma-config-tmp/g`"
    local CARMA_BACKGROUND_DOCKER_FILE="`docker run --rm --volumes-from carma-config-tmp:ro --entrypoint sh busybox:latest -c 'cat /opt/carma/vehicle/config/docker-compose-background.yml' | sed s/carma-config/carma-config-tmp/g`"

    echo "Downloading $IMAGE_NAME dependencies..."
    echo "$CARMA_DOCKER_FILE" | docker-compose -f - pull --ignore-pull-failures
    echo "$CARMA_BACKGROUND_DOCKER_FILE" | docker-compose -f - pull --ignore-pull-failures

    echo "Cleaning up temporary container..."
    docker rm carma-config-tmp
}
carma__fix_bag() {
    # following script finds the most recently modified .bag.active or .bag file in /opt/carma/logs directory
    last_modified_file="$(find "/opt/carma/logs" -type f \( -name \*.bag.active -o -name \*.bag \) -printf "%T@ %p\0" | awk 'BEGIN {RS="\0";} {if (NR==1){minmtime=$1; $1=""; lastmodified=$0;} else if (minmtime<$1){minmtime=$1; $1=""; lastmodified=$0;}} END{print substr(lastmodified,2)}')"

    if [[ ${last_modified_file} == *.bag ]]; then
        printf "The last modified file is named %s\n" "${last_modified_file}"
        printf "Since it is not .bag.active, there is nothing to fix. Nothing changed.\n"
    elif [[ ${last_modified_file} == *.bag.active ]]; then
        printf "The last modified file is named %s\n" "${last_modified_file}"
        rosbag reindex ${last_modified_file}

        rosbag_name="$(basename -s .bag.active ${last_modified_file})"

        if test -f "/opt/carma/logs/${rosbag_name}.bag.orig.active"; then
            rm "/opt/carma/logs/${rosbag_name}.bag.orig.active"
        fi

        mv ${last_modified_file} "/opt/carma/logs/${rosbag_name}.bag"

        printf "Created a rosbag named %s\n" "/opt/carma/logs/${rosbag_name}.bag"
    elif [[ ${last_modified_file} == "" ]]; then

        printf "There is no .bag.active or .bag files in /opt/carma/logs directory. Nothing changed.\n"

    fi
}

__get_compose_env_files() {
    # If variable is not set, modify to get default values from carma-config
    RETURN_COMPOSE_ENV_FILES="/tmp/.env"

    # If .env file exists in current directory, it takes higher precedence over carma-config .env file
    if [ -f ".env" ]; then
        echoerr "NOTE: .env file detected in current directory, including its values for consideration for current carma session."
        RETURN_COMPOSE_ENV_FILES="$RETURN_COMPOSE_ENV_FILES,.env"
    fi

    # If user defined COMPOSE_ENV_FILES exists in the shell, it takes higher precedence over .env from carma-config and current directory .env
    if [ -n "${COMPOSE_ENV_FILES+x}" ]; then
        echoerr "WARNING: COMPOSE_ENV_FILES is already defined in the shell: $COMPOSE_ENV_FILES.
            Setting it higher precedence than .env from carma-config and current directory .env (if it exists)"
        # This way, user defined COMPOSE_ENV_FILES still takes higher precedence.
        RETURN_COMPOSE_ENV_FILES="$RETURN_COMPOSE_ENV_FILES,$COMPOSE_ENV_FILES"
    fi

    echo "$RETURN_COMPOSE_ENV_FILES"
}

carma__start() {
    if ! docker container inspect carma-config > /dev/null 2>&1; then
        echo "No existing CARMA configuration found, nothing to start. Please set a config."
        echo "Done."
        exit 1
    fi

    # Capture all extra arguments
    EXTRA_ARGS=("$@")

    # Extract the .env file content from the carma-config volume and save temporarily
    docker run --rm --volumes-from carma-config:ro busybox:latest cat /opt/carma/vehicle/config/.env > /tmp/.env 2>/dev/null  # suppress misleading error message that will occur for versions before 4.5.0

    # Get correct order of .env file precedence. From low to high: carma-config .env, local .env, user defined.
    COMPOSE_ENV_FILES_WITH_DEFAULT=$(__get_compose_env_files)

    echo "Starting CARMA background processes..."
    docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
    'cat /opt/carma/vehicle/config/docker-compose-background.yml' | \
        COMPOSE_ENV_FILES="$COMPOSE_ENV_FILES_WITH_DEFAULT", docker-compose -f - -p carma-background "${EXTRA_ARGS[@]:1}" up -d

    if [[ "${EXTRA_ARGS[0]}" == "all" ]]; then
        shift
        echo "Starting CARMA Platform foreground processes..."
        docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
        'cat /opt/carma/vehicle/config/docker-compose.yml' | \
            COMPOSE_ENV_FILES="$COMPOSE_ENV_FILES_WITH_DEFAULT", docker-compose -f - -p carma "${EXTRA_ARGS[@]:1}" up
    elif [[ ! -z "${EXTRA_ARGS[0]}" ]]; then
        echo "Unrecognized argument \"start ${EXTRA_ARGS[0]}\""
    fi

    rm /tmp/.env
}

carma__stop() {
    if ! docker container inspect carma-config > /dev/null 2>&1; then
        echo "No existing CARMA configuration found, nothing to stop. Please set a config."
        echo "Done."
        exit 1
    fi

    # Extract the .env file content from the carma-config volume
    docker run --rm --volumes-from carma-config:ro busybox:latest cat /opt/carma/vehicle/config/.env > /tmp/.env 2>/dev/null  # suppress misleading error message that will occur for versions before 4.5.0

    # Get correct order of .env file precedence. From low to high: carma-config .env, local .env, user defined.
    COMPOSE_ENV_FILES_WITH_DEFAULT=$(__get_compose_env_files)

    echo "Shutting down CARMA processes..."
    docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
    'cat /opt/carma/vehicle/config/docker-compose.yml' | \
    COMPOSE_ENV_FILES="$COMPOSE_ENV_FILES_WITH_DEFAULT", docker-compose -f - -p carma down

    if [ "$1" = "all" ]; then
        echo "Shutting down CARMA background processes..."
        docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
        'cat /opt/carma/vehicle/config/docker-compose-background.yml' | \
        COMPOSE_ENV_FILES="$COMPOSE_ENV_FILES_WITH_DEFAULT", docker-compose -f - -p carma-background down
        if [ "$2" = "fix_bag" ]; then
            echo "Trying to fix the last rosbag file..."
            carma__fix_bag
        elif [ ! -z "$2" ]; then
            echo "Unrecognized argument \"stop all $2\""
        fi
    elif [ "$1" = "fix_bag" ]; then
        echo "Trying to fix the last rosbag file..."
        carma__fix_bag
    elif [ ! -z "$1" ]; then
        echo "Unrecognized argument \"stop $1\""
    fi

    rm /tmp/.env
}

carma__ps() {
    if ! docker container inspect carma-config > /dev/null 2>&1; then
        echo "No existing CARMA configuration found, nothing to report. Please set a config."
        echo "Done."
        exit 1
    fi

    echo "CARMA Background Processes:"
    docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
    'cat /opt/carma/vehicle/config/docker-compose-background.yml' | \
    docker-compose -f - -p carma-background ps

    echo "CARMA Foreground Processes:"
    docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
    'cat /opt/carma/vehicle/config/docker-compose.yml' | \
    docker-compose -f - -p carma ps
}

carma-config__status() {
    if ! docker container inspect carma-config > /dev/null 2>&1; then
        echo "No existing CARMA configuration found, nothing to report. Please set a config."
        echo "Done."
        exit 1
    fi

    if [ -z "$1" ]; then
        local CURRENT_IMAGE=`docker container inspect --format='{{.Config.Image}}' carma-config`
        echo "Current configuration is loaded from image: $CURRENT_IMAGE"
        echo ""
        echo "  -- docker-compose.yml:"
        docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
        'cat /opt/carma/vehicle/config/docker-compose.yml'
        echo "  -- docker-compose-background.yml:"
        docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
        'cat /opt/carma/vehicle/config/docker-compose-background.yml'
        echo "  -- carma.launch:"
        docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
        'cat /opt/carma/vehicle/config/carma.launch'
        echo "  -- carma.config.js:"
        docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
        'cat /opt/carma/vehicle/config/carma.config.js'
        echo "  -- carma.urdf:"
        docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
        'cat /opt/carma/vehicle/config/carma.urdf'
    else
        docker run --rm --volumes-from carma-config:ro --entrypoint sh busybox:latest -c \
        "cat /opt/carma/vehicle/config/$1"
    fi
}

carma__autoupdate_stop() {
  crontab -l | grep -v "docker pull" | crontab -
  echo "Docker image auto-updates stopped."
}

carma__autoupdate_on() {
  local hour="$1"
  local minute="$2"

  if ! [[ "$hour" =~ ^([0-1]?[0-9]|2[0-3])$ ]] || ! [[ "$minute" =~ ^([0-5]?[0-9])$ ]]; then
    echo "Invalid hour or minute. Use hour (0-23) and minute (0-59)."
    return 1
  fi
  COMPOSE_ENV_FILES_WITH_DEFAULT="$(pwd)/../.env"

  # Schedule the Docker image updates using cron
  (crontab -l 2>/dev/null; echo "${minute} ${hour} * * * docker run --rm --volumes-from carma-config:ro --entrypoint cat busybox:latest /opt/carma/vehicle/config/docker-compose.yml | COMPOSE_ENV_FILES="$COMPOSE_ENV_FILES_WITH_DEFAULT", docker compose -f - pull --ignore-pull-failures") | crontab -

  echo "Docker image updates scheduled for $hour:$minute daily."
}

carma__exec() {
    local USERNAME=usdotfhwastol
    local TAG=latest

    local TARGET_IMAGE="$USERNAME/carma-platform:$TAG"
    local NETWORK="host"  # Default network mode
    local RUNTIME=""      # Default runtime is empty
    local COMMAND=""
    local INIT_FILE="/home/carma/.base-image/init-env.sh"

    # Process all arguments in any order
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --gui)
                RUNTIME="nvidia"
                echo "Setting Docker runtime to $RUNTIME"
                shift
                ;;
            --network|-n)
                if [[ -z "$2" ]]; then
                    echo "When --network or -n is specified, you must enter a network name."
                    exit 1
                fi
                NETWORK="$2"
                echo "Setting Docker network to $NETWORK"
                shift 2
                ;;
            -i|--image)
                if [[ -z "$2" ]]; then
                    echo "When -i or --image is specified, you must enter a CARMA Docker image and tag."
                    exit 1
                fi
                TARGET_IMAGE="$2"
                echo "Setting Docker image to $TARGET_IMAGE"
                shift 2
                ;;
            *)
                # Assume any remaining arguments are part of the command
                COMMAND="${COMMAND} $1"
                shift
                ;;
        esac
    done

    # If no custom image is specified, get the default from the config
    if [[ "$TARGET_IMAGE" == "$USERNAME/carma-platform:$TAG" ]]; then
        TARGET_IMAGE=$(__get_image_from_config 'carma-platform\|carma-messenger')
    fi

    # Verify target image exists
    if [[ -z "$(docker images -q $TARGET_IMAGE 2> /dev/null)" ]]; then
        echo "Specified image could not be found. CARMA exec attempted to use image $TARGET_IMAGE but it was not found."
        echo "If an alternative image is available, please specify it using the -i or --image option."
        exit 1
    fi

    # Setup for Docker X11 and authorization
    local XSOCK=/tmp/.X11-unix
    local XAUTH=/tmp/.docker.xauth
    touch $XAUTH
    xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -

    echo "Trying to open shell inside image: $TARGET_IMAGE"

    # Set entrypoint and environment setup
    local entrypoint="/bin/bash"
    if [[ -n "$COMMAND" ]]; then
        COMMAND="source $INIT_FILE && $COMMAND"

        docker run \
            -e DISPLAY=$DISPLAY \
            --runtime=$RUNTIME \
            --group-add video \
            --volume=$XSOCK:$XSOCK:rw \
            --volume=$XAUTH:$XAUTH:rw \
            --volume=/opt/carma/logs:/opt/carma/logs \
            --volume=/opt/carma/.ros:/opt/carma/.ros \
            --volume=/opt/carma/vehicle/calibration:/opt/carma/vehicle/calibration \
            --volume=/opt/carma/yolo:/opt/carma/yolo \
            --volume=/opt/carma/maps:/opt/carma/maps \
            --volume=/opt/carma/routes:/opt/carma/routes \
            --env="XAUTHORITY=${XAUTH}" \
            --env QT_X11_NO_MITSHM=1 \
            --device=/dev/dri:/dev/dri \
            --network=$NETWORK \
            --entrypoint="$entrypoint" \
            -it $TARGET_IMAGE -c "$COMMAND"
    else
        docker run \
            -e DISPLAY=$DISPLAY \
            --runtime=$RUNTIME \
            --group-add video \
            --volume=$XSOCK:$XSOCK:rw \
            --volume=$XAUTH:$XAUTH:rw \
            --volume=/opt/carma/logs:/opt/carma/logs \
            --volume=/opt/carma/.ros:/opt/carma/.ros \
            --volume=/opt/carma/vehicle/calibration:/opt/carma/vehicle/calibration \
            --volume=/opt/carma/yolo:/opt/carma/yolo \
            --volume=/opt/carma/maps:/opt/carma/maps \
            --volume=/opt/carma/routes:/opt/carma/routes \
            --env="XAUTHORITY=${XAUTH}" \
            --device=/dev/dri:/dev/dri \
            --network=$NETWORK \
            --entrypoint="$entrypoint" \
            -it $TARGET_IMAGE --init-file "$INIT_FILE"
    fi
}


carma__help() {
    cat <<HELP
-------------------------------------------------------------------------------
| USDOT FHWA STOL CARMA Platform                                              |
-------------------------------------------------------------------------------

Please enter one of the following commands:
    config:
        status (filename)
            - Report the current configuration status in total or for the
              specified file
        list_local
            - List available usdotfhwastol configurations on the host machine
            -d
                - List available usdotfhwastoldev configurations on the host machine
            -c
                - List available usdotfhwastolcandidate configurations on the host machine
        list_remote
            - List available usdotfhwastol configurations on Dockerhub
            -d
                - List available usdotfhwastoldev configurations on Dockerhub
            -c
                - List available usdotfhwastolcandidate configurations on Dockerhub
        install <tag/image>
            - Install a configuration identified by <tag> and download
              dependencies. If <tag> is bare (no :), it is assumed to be a
              usdotfhwastol/carma-config tag.
            -d
                - tag organization is assumed to be usdotfhwastoldev
            -c
                - tag organization is assumed to be usdotfhwastolcandidate
        set <tag/image>
            - Set the configuration to the version identified by <tag>. If
              <tag> is bare (no :), it is assumed to be a
              usdotfhwastol/carma-config tag.
        edit
            - Open a shell inside the current configuration storage with r/w
              permissions
        inspect
            - Open a shell inside the current configuration storage with r/o
              permissions
            -d
                - uses a usdotfhwastoldev/carma-base image
            -c
                - uses a usdotfhwastolcandidate/carma-base image
        reset
            - Restore a configuration to its default state
    start (all (docker-compose up args))
        - Start the CARMA platform's background processes. If all, start
          everything.
        - Accepts the same flags as "docker-compose up"
        - NOTE: Uses COMPOSE_ENV_FILES under the hood for env files.
          Precedence from lowest to highest: .env in carma-config, local .env, existing COMPOSE_ENV_FILES in shell.
    stop (all (docker-compose down args))
        - Stop the CARMA platform's foreground processes. If all, stop
          everything.
        - Accepts the same flags as "docker-compose down"
        - NOTE: Uses COMPOSE_ENV_FILES under the hood for env files.
          Precedence from lowest to highest: .env in carma-config, local .env, existing COMPOSE_ENV_FILES in shell.
        - fix_bag
            - After stopping carma, checks if the last run's rosbag has been correctly saved. If not, fixes it to be replayable.
    exec <optional bash command>
        - Opens a new container with GUI support from the carma-platform or carma-messenger image with the version specified in docker compose. If no version is found it will use latest.
          This container can be used to interact with CARMA using ROS tooling such as Rviz and RQT.
        - If --gui argument is provided it will start docker using nvidia runtime. this flag should be provided as the first flag to work.
        - If the -i <image> argument is provided then the target image will be used instead
        -d
            - uses a usdotfhwastoldev image
        -c
            - uses a usdotfwhastolcandidate image
    ps
        - List all running CARMA docker containers
    attach
        - View STDOUT from all running CARMA foreground processes
    list_extensions
        - List the locally available script extensions
    <script extension>
        - Script extensions can be added by placing a file in ~/.carma_script_extensions/ with the name of the new subcommand and no file extension.
          The file should contain a matching carma__<script extension>() function to recieve the commands.
    autoupdate
        - Must excecute inside cdasim_config folder
        - _stop
            - Stop the auto-update cron job
        - _on <hour> <minute>
            - Start the auto-update cron job at the specified hour and minute
            - Hour: 0-23
            - Minute: 0-59
    help
        - Display this information"
HELP
}

carma__config() {
    local cmdname=$1; shift
    if type "carma-config__$cmdname" >/dev/null 2>&1; then
        "carma-config__$cmdname" "$@"
    else
        carma__help
        exit 1
    fi
}

carma__list_extensions() {
    # List all executable files in the extensions directory
    find "${CARMA_SCRIPT_EXTENSIONS_DIR}/" -executable -type f | sed "s|${CARMA_SCRIPT_EXTENSIONS_DIR}/||"
}


carma() {
    local cmdname=$1; shift
    if type "carma__$cmdname" >/dev/null 2>&1; then
        "carma__$cmdname" "$@"
    else
        carma__help
        exit 1
    fi
}

# if the functions above are sourced into an interactive interpreter, the user can
# just call "carma-config set" or "carma-config reset" with no further code needed.

# if invoked as a script rather than sourced, call function named on argv via the below;
# note that this must be the first operation other than a function definition
# for $_ to successfully distinguish between sourcing and invocation:
[[ $_ != $0 ]] && return

# Check if a matching script extension is available to be checked
CARMA_SCRIPT_EXTENSIONS_DIR="/home/$USER/.carma_script_extensions"
CARMA_SCRIPT_EXTENSIONS_FILE="${CARMA_SCRIPT_EXTENSIONS_DIR}/$1"
if [ -f "$CARMA_SCRIPT_EXTENSIONS_FILE" ]; then
    source ${CARMA_SCRIPT_EXTENSIONS_FILE}
fi

# make sure we actually *did* get passed a valid function name
if declare -f "carma__$1" >/dev/null 2>&1; then
  # invoke that function, passing arguments through
  "carma__$@" # same as "$1" "$2" "$3" ... for full argument list
else
    carma__help
    exit 1
fi
